// 接口

interface Animal {
    eat: string;
}

// 接口可以继承接口, 接口中定义的字段实现类中必须实现
interface Person extends Animal {
    age: number;
}

// 在字段名后面加"?"号表示字段是可以不实现的
interface HomoSapiens extends Person {
    sex: number;
    name?: string;
}

// 类实现接口使用implements关键字
class Man implements HomoSapiens {
    sex: number = 1
    age: number = 18
    eat: string = '吃吃吃'
}

// 只读属性, 只有在实现类初始化的时候设定值, 之后不再允许修改
interface Dog extends Animal {
    readonly name: string;
}

// 接口可以直接当成类型使用, 创建对象
const labrador: Dog = { name: '阿猪', eat: '吃吃吃' }
// 只读属性不能被修改, 所以这里会报错
// labrador.name = '小猪'

// 如果使用接口用作接收的类型, 传递的对象没有接口中的属性字段ts默认会报错
function walk_dog(dog: Dog) {
    console.log(JSON.stringify(dog))
}
// walk_dog({ name: '阿猪', eat: '吃吃吃', aaa: 1 })

// 如果并不确定接收的属性字段有什么, 最推荐的方式就是添加一个可接受任意数量任意类型属性的属性字段
interface Cat extends Animal {
    name: string;
    // 接收任意类型数量的属性字段定义格式为: [随便起的名字: 属性名类型几乎都是string]: any
    [property: string]: any
}

// 这样就可以接受任意数量的属性字段了
function walk_cat(cat: Cat) {
    console.log(JSON.stringify(cat))
}
walk_cat({ name: '阿猪', eat: '吃吃吃', aaa: 1, bbb: 2 })
// 也可以使用断言的方式
walk_dog({ name: '阿猪', eat: '吃吃吃', aaa: 1 } as Dog)

// 函数接口
interface Add {
    (a: number, b: number): number
}

// 函数接口只能通过lambda或匿名函数的方式来实现
const add1: Add = (a: number, b: number): number => a + b
const add2: Add = function (a: number, b: number): number {
    return a + b
}

// 多个接口可以起同一个名称
interface Interface {
    name: string
}
interface Interface {
    age: number
}
interface Interface {
    haha(): void
}
// 定义class或定义对象时实现一个就相当于实现同名的所有接口, 这种就是接口的合并
class Clazz implements Interface {
    name: string = 'haha'
    age: number = 18
    haha() {
        console.log('haha')
    }
}
const obj1: Interface = {
    name: 'haha',
    age: 18,
    haha() {
        console.log('haha')
    }
}